# coding: utf-8
from time import sleep
from typing import Any, Callable, List

from PySide6.QtCore import QThread, QRect, Signal, Qt, QTimer, QSize
from PySide6.QtGui import QColor
from PySide6.QtWidgets import QWidget, QVBoxLayout, QLayout, QHBoxLayout
from qfluentwidgets import SubtitleLabel, TransparentToolButton, FluentIcon, BodyLabel, SmoothScrollArea

from common import WorkerThread
from components import ZoomImageLabel, GridLayout, Alert
from .card import WallpaperCard
from .loading_dialog import LoadingDialog


class LazyThread(QThread):
    loadSignal = Signal(ZoomImageLabel, bool)

    def __init__(self, parent=None):
        super().__init__(parent)
        self.viewRect = QRect()
        self.lazyWidgets = []  # type: List[QWidget]

    def setData(self, widgets: List[QWidget], viewRect: QRect):
        self.viewRect = viewRect
        self.lazyWidgets = widgets
        if not self.isRunning():
            self.start()

    def run(self):
        for item in self.lazyWidgets:
            try:
                item_rect = item.geometry()
                if self.viewRect.intersects(item_rect):
                    self.loadSignal.emit(item, True)
                else:
                    self.loadSignal.emit(item, False)
                # widget 过多时会造成卡顿，这里延迟0.01秒加载
                sleep(0.01)
            except RuntimeError as e:
                pass


class BaseInterface(SmoothScrollArea):
    reloadSignal = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.worker = WorkerThread(self)
        self.verticalLayout = QVBoxLayout(self)
        self.topLayout = QHBoxLayout()
        self.bottomLayout = QVBoxLayout()

        self.view = QWidget(self)
        self.topBtn = TransparentToolButton(FluentIcon.UP, self)
        self.reloadBtn = TransparentToolButton(FluentIcon.UPDATE, self)

        self.lazyWidgets = []  # type: List[QWidget]
        self.lazyThread = LazyThread(self)
        self.lazyTimer = QTimer(self)
        self._loaded = False
        self.loadingDialog = None  # type: LoadingDialog

        self.setWidget(self.view)
        self.setWidgetResizable(True)
        self.view.setAutoFillBackground(False)
        self.enableTransparentBackground()

        self.worker.result.connect(self.onResult)
        self.worker.failed.connect(self.onFailed)
        self.worker.finished.connect(self.onFinished)
        self.worker.started.connect(self.onStarted)

        self.lazyTimer.setInterval(200)
        self.lazyTimer.setSingleShot(True)
        self.lazyTimer.timeout.connect(self._updateLazyWidgets)
        self.lazyThread.loadSignal.connect(self._onLazyLoad)
        self.verticalScrollBar().valueChanged.connect(self.lazyTimerStart)
        self.verticalScrollBar().valueChanged.connect(self.onVerticalScrollValueChange)

        self.reloadBtn.resize(40, 40)
        self.reloadBtn.setIconSize(QSize(20, 20))
        self.reloadBtn.clicked.connect(self.onReload)
        self.topBtn.resize(40, 40)
        self.topBtn.setIconSize(QSize(20, 20))
        self.topBtn.clicked.connect(lambda: self.verticalScrollBar().setValue(0))

        self.topLayout.setAlignment(Qt.AlignmentFlag.AlignLeft)
        self.topLayout.setContentsMargins(0, 0, 0, 0)
        self.bottomLayout.setAlignment(Qt.AlignmentFlag.AlignRight | Qt.AlignmentFlag.AlignBottom)
        self.bottomLayout.setContentsMargins(0, 0, 0, 0)

        self.bottomLayout.addWidget(self.topBtn)
        self.bottomLayout.addWidget(self.reloadBtn)

        self.verticalLayout.addLayout(self.topLayout)
        self.verticalLayout.addStretch(1)
        self.verticalLayout.addLayout(self.bottomLayout)

    def load(self, func, *args, slot: Callable = None, **kwargs):
        self.worker.addTask(func, *args, slot=slot, **kwargs)

    def onResult(self, slot: Callable, result: Any):
        if slot:
            slot(result)

    def onFailed(self, error: str):
        print(error)
        Alert.error(error)

    def onFinished(self):
        self.loadingDialog.close()
        self.loadingDialog.deleteLater()
        self.loadingDialog = None

    def onStarted(self):
        self.loadingDialog = LoadingDialog(self)
        self.loadingDialog.exec()

    def onReload(self):
        self.reloadSignal.emit()

    def _updateLazyWidgets(self):
        if not self.lazyWidgets:
            return
        rect = self.viewport().rect()
        rect.translate(self.horizontalScrollBar().value(), self.verticalScrollBar().value())
        self.lazyThread.setData(self.lazyWidgets, rect)
        self.updateGeometry()

    def _onLazyLoad(self, item: QWidget, visible: bool):
        if hasattr(item, "setLazyVisible"):
            item.setLazyVisible(visible)

    def lazyTimerStart(self):
        if self.lazyTimer.isActive():
            self.lazyTimer.stop()
        self.lazyTimer.start()

    def resizeEvent(self, event):
        super().resizeEvent(event)
        self.lazyTimerStart()

    def setLoaded(self, loaded: bool):
        self._loaded = loaded

    def isLoaded(self):
        return self._loaded

    def addLabel(self, url: str, layout: QLayout, size: QSize = QSize(300, 200)):
        label = WallpaperCard(self.view)
        label.setDataSrc(url)
        layout.addWidget(label)
        if size:
            label.resize(size)
        label.finished.connect(self.onLabelFinished)
        self.lazyWidgets.append(label)
        return label

    def onVerticalScrollValueChange(self, value: int):
        if value == self.verticalScrollBar().maximum() and not self.worker.isRunning():
            self.nextPage()

    def nextPage(self):
        pass

    def onLabelFinished(self, label: WallpaperCard):
        label.setMinimumSize(300, 180)


class WallpaperInterface(BaseInterface):
    returnSignal = Signal()

    def __init__(self, parent=None):
        super().__init__(parent)
        self.viewLayout = QVBoxLayout(self.view)
        self.returnBtn = TransparentToolButton(FluentIcon.RETURN, self)
        self.titleLabel = SubtitleLabel(self)
        self.descriptionLabel = BodyLabel(self)
        self.waterfallLayout = GridLayout()

        self.descriptionLabel.setTextColor(QColor(100, 100, 100, 200), QColor(255, 255, 255, 200))

        self.viewLayout.addWidget(self.titleLabel)
        self.viewLayout.addWidget(self.descriptionLabel)
        self.viewLayout.addLayout(self.waterfallLayout)

        self.reloadBtn.setFixedSize(40, 40)

        self.topLayout.addWidget(self.returnBtn)
        self.topLayout.addWidget(self.titleLabel)
        self.topLayout.addWidget(self.descriptionLabel)
        self.topLayout.addLayout(self.waterfallLayout)

        self.setViewportMargins(0, 50, 0, 0)

        self.returnBtn.clicked.connect(self.returnSignal)

    def setTitle(self, title: str):
        self.titleLabel.setText(title)

    def setDescription(self, description: str):
        self.descriptionLabel.setText(description)
        if not self.descriptionLabel.text():
            self.descriptionLabel.hide()
        else:
            self.descriptionLabel.show()

    def clear(self):
        self.waterfallLayout.takeAllWidgets()
        self.lazyWidgets.clear()
        self.waterfallLayout.update()

    def addLabel(self, url: str):
        return super().addLabel(url, self.waterfallLayout)

    def _updateLazyWidgets(self):
        super()._updateLazyWidgets()
        self.waterfallLayout.update()
